class SelectSockets {
	public static void main(String[] args) throws Exception {
		new SelectSockets().runServer(args);
	}
	
	public static final int PORT_NUMBER = 1234;
 
	public void runServer(String[] args) throws Exception {
 
		int port = PORT_NUMBER;
 
		if (args.length > 0) { // 覆盖默认的监听端口
			port = Integer.parseInt(args[0]);
		}
 
		try (Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open()) {
 
			serverChannel.configureBlocking(false);// 设置非阻塞模式
			
			serverChannel.register(selector, SelectionKey.OP_ACCEPT);// 将ServerSocketChannel注册到Selector
 
			System.out.printf("Listening on port %d\n", port);
			ServerSocket serverSocket = serverChannel.socket();// 得到一个ServerSocket去和它绑定
			serverSocket.bind(new InetSocketAddress(port));// 设置server channel将会监听的端口
 
			// 此处要求Java11及以上版本,否则自行修改
			while (true) selector.select(this::doSelection);
		}
	}
 
	protected void doSelection(SelectionKey key) {
		try {
			// 判断是否是一个连接到来
			if (key.isAcceptable()) {
				this.accept(key);
			}
			// 判断这个channel上是否有数据要读
			else if (key.isReadable()) {
				this.readDataFromSocket(key);
			}
			else if (key.isWritable()) {
				this.writeDataToSocket(key);
			}
		} catch (IOException e) {
			throw new RuntimeException("我不确定出了什么问题，这里有bug", e);
		}
 
	}
	
	
	protected void accept(SelectionKey key) throws IOException {
		ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
		SocketChannel channel = ssc.accept();
		// 注册读事件
		if (channel == null) return;
		// 设置通道为非阻塞
		channel.configureBlocking(false);
		// 将通道注册到选择器上
		channel.register(key.selector(), SelectionKey.OP_READ);
	}
	private static final int L = 1024;
	
	protected void readDataFromSocket(SelectionKey key) throws IOException {
		
		SocketChannel socketChannel = (SocketChannel) key.channel();
		
		// 嘛呀，处理输入这么麻烦的吗？？？
		List<byte[]> list = new ArrayList<>();
		ByteBuffer buffer = ByteBuffer.allocateDirect(L);
		while (socketChannel.read(buffer) > 0) {
			buffer.flip();
			byte[] bytes = new byte[L];
			buffer.get(bytes, 0, buffer.remaining());
			list.add(bytes);
			buffer.compact();
		}
		byte[] bytes = new byte[L * list.size()];
		for(int i=0; i<list.size(); i++) {
			System.arraycopy(list.get(i), 0, bytes, i*L, L);
		}
		System.out.println(new String(bytes, "utf-8"));
		
		key.interestOps(SelectionKey.OP_WRITE);
	}
	
	
	private final String header = "HTTP/1.1 200 OK\r\n" + 
			"Server: nginx/1.13.7\r\n" + 
			"Date: Sat, 30 Mar 2019 09:50:12 GMT\r\n" + 
			"Content-Type: text/html\r\n" + 
			//"Content-Length: 612\r\n" + 
			"Last-Modified: Sun, 17 Mar 2019 10:40:32 GMT\r\n" + 
			"Connection: keep-alive\r\n" + 
			"ETag: \"5c8e2420-264\"\r\n" + 
			"Accept-Ranges: bytes\r\n\r\n";
 
	protected void writeDataToSocket(SelectionKey key) throws IOException {
		
		SocketChannel socketChannel = (SocketChannel) key.channel();
		
		// 我该返回点什么
		byte[] bytes = (header+"hello, here is some word").getBytes("utf-8");
		ByteBuffer sender = ByteBuffer.wrap(bytes);
		sender.put(bytes);
		sender.flip();
		socketChannel.write(sender);
		
		//socketChannel.shutdownOutput();
		socketChannel.close();
		
		key.cancel();
	}
 
}
